﻿/**
 * @file guild_module_saver.hpp
 * @date 2021/05/11 10:05
 *
 * @author harrybao (harrybao@123u.com)
 *
 * @brief 公会模块保存
 *
 * @version v1.0.0
 *
 * @copyright Copyright © 2021 All Rights Reserved 上海欢乐互娱网络科技有限公司
 *
 * @par history:
 * <table>
 * <tr><th>Date                     <th>Version     <th>Author <th>Description
 * <tr><td>2021/05/11 10:05         <td>1.0.0       <td>harrybao
 * <td>创建初始版本
 * </table>
 */

// #ifndef GUILD_MODULE_SAVER_HPP
// #define GUILD_MODULE_SAVER_HPP

#pragma once

#include <cassert>
#include <sstream>

#include "base64/base64.h"
#include "guild/guild.h"
#include "log/service_logger.hpp"
#include "orm/guild_module_orm.hpp"
#include "pool/service_data_pool_op.hpp"
#include "redis/service_redis_fwd.hpp"


namespace guild_service {
namespace detail {

// TODO bench Load && Save

/**
 * @brief 同步保存公会模块
 *
 * @tparam Module 公会模块模板
 * @param service_name 公会服务名
 * @param data 需要序列化的公会模块类
 * @param guild_id 公会id
 * @param module_name 公会模块名
 * @return true 保存成功
 * @return false 保存失败
 */
template <typename Module>
bool SaveGuildModule(const std::string &service_name, const Module &data,
                     const guild_id_type guild_id,
                     const std::string &module_name) {
  // Serializer serializer;
  std::string pb_str;
  if (!SerializeToString(data, pb_str)) {
    LOG_FAIL("公会模块序列化失败 公会id:%llu 模块名:%s", guild_id,
             module_name.c_str());
    return false;
  }

#ifdef DEBUG
  assert(!pb_str.empty());
#endif  // DEBUG

  if (pb_str.empty()) {
    LOG_FAIL("公会模块序列化失败，pb字符串为空 公会id:%llu 模块名:%s", guild_id,
             module_name.c_str());
    return false;
  }
  pb_str = base64_encode((unsigned char *)pb_str.c_str(), static_cast<unsigned int>(pb_str.size()));

  std::stringstream ss;
  ss << "HSET"
     << " guild:" << guild_id << " " << module_name << " " << pb_str;

  auto ret = redis_command_co(service_name, ss.str(), kRedisCommonTimeOut);
  if (!ret) {
    LOG_FAIL("redis处理失败 命令:(%s)", ss.str().c_str());
    return false;
  }

  if (!ret->is_success()) {
    LOG_FAIL("redis处理失败 命令:(%s) redis命令:(%s) 错误:%s 错误码:%u",
             ss.str().c_str(), ret->get_command().c_str(),
             ret->get_error().c_str(),
             static_cast<uint32_t>(ret->get_error_code()));
    return false;
  }

  LOG_DEBUG("redis同步保存公会模块成功 命令:(%s) 模块名:%s", ss.str().c_str(),
            module_name.c_str());

  return true;
}

template <size_t Index, typename Tuple, typename Func>
void tuple_at(const Tuple &t, Func f) {
  const auto &v = std::get<Index>(t);
  std::invoke(f, v);
}

template <typename Tuple, typename Func, size_t Index = 0> void tuple_for_each(const Tuple& t, const Func& f) {
  constexpr auto n = std::tuple_size_v<Tuple>;
  if constexpr(Index < n) {
    tuple_at<Index>(t, f);
    tuple_for_each<Tuple, Func, Index+1>(t, f);
  }
}

/**
 * @brief 同步保存公会模块列表
 *
 * @tparam Module 公会模块模板
 * @param service_name 公会服务名
 * @param data_list 需要序列化的公会模块类列表
 * @param guild_id 公会id
 * @param module_name 公会模块名列表
 * @return true 保存成功
 * @return false 保存失败
 */
template <typename ModuleList>
bool SaveGuildModuleList(const std::string &service_name,
                         const ModuleList &data_list,
                         const guild_id_type guild_id,
                         const std::vector<std::string> &module_name_list) {

  std::vector<std::string> module_pb_str_list;
  tuple_for_each(data_list, [&module_pb_str_list, guild_id](const auto &v) -> bool {
    std::string pb_str;
    if (!SerializeToString(v, pb_str)) {
      LOG_FAIL("公会模块序列化失败 公会id:%llu", guild_id);
      return false;
    }

#ifdef DEBUG
    assert(!pb_str.empty());
#endif  // DEBUG

    if (pb_str.empty()) {
      LOG_FAIL("公会模块序列化失败，pb字符串为空 公会id:%llu", guild_id);
      return false;
    }

    pb_str = base64_encode((unsigned char *)pb_str.c_str(),
                           static_cast<unsigned int>(pb_str.size()));
    
    module_pb_str_list.emplace_back(std::move(pb_str));

    return true;
  });

  if (module_pb_str_list.size() != module_name_list.size() || module_pb_str_list.empty()) {
    return false;
  }

  std::stringstream ss;
  ss << "HMSET" << " guild:" << guild_id;
  for (std::size_t i = 0; i < module_pb_str_list.size(); ++i) {
    ss << " " << module_name_list[i] << " " <<  module_pb_str_list[i];
  }

  auto ret = redis_command_co(service_name, ss.str(), kRedisCommonTimeOut);
  if (!ret) {
    LOG_FAIL("redis处理失败 命令:(%s)", ss.str().c_str());
    return false;
  }

  if (!ret->is_success()) {
    LOG_FAIL("redis处理失败 命令:(%s) redis命令:(%s) 错误:%s 错误码:%u",
             ss.str().c_str(), ret->get_command().c_str(),
             ret->get_error().c_str(),
             static_cast<uint32_t>(ret->get_error_code()));
    return false;
  }

  LOG_DEBUG("redis同步保存公会模块成功 命令:(%s)", ss.str().c_str());

  return true;
}

/**
 * @brief 异步保存公会模块
 *
 * @tparam Serializer 序列化处理类模板
 * @tparam Serializer::DataType 需要序列化的数据结构类模板
 * @param service_name 公会服务名
 * @param data 需要序列化的数据结构类
 * @param guild_id 公会id
 * @param handler redis执行回调函数
 * @return true 保存成功
 * @return false 保存失败
 */
template <typename Module>
bool SaveGuildModuleAsync(const std::string &service_name, const Module &data,
                          const guild_id_type guild_id,
                          const std::string &module_name,
                          const redis_handler_type &handler) {
  std::string pb_str;
  if (!SerializeToString(data, pb_str)) {
    LOG_FAIL("公会模块序列化失败 公会id:%s 模块名:%s", guild_id,
             module_name.c_str());
    return false;
  }
  pb_str = base64_encode((unsigned char *)pb_str.c_str(), pb_str.size());

  std::stringstream ss;
  ss << "HSET"
     << " guild:" << guild_id << " " << module_name;

  assert(!pb_str.empty());

  ss << " " << pb_str;

  if (!redis_command_async(service_name, ss.str(), 0, handler, 0)) {
    LOG_FAIL("redis执行失败 命令:(%s)", ss.str().c_str());
    return false;
  }

  LOG_DEBUG("redis异步保存公会模块成功 命令:(%s) 模块名:%s", ss.str().c_str(),
            module_name.c_str());

  return true;
}

}  // namespace detail
}  // namespace guild_service

// #endif  // GUILD_MODULE_SAVER_HPP